#include <stdio.h>
#include <stdlib.h>
#include "TI_Command.h"

extern void Nu_DmaBufFree(physaddr_t ptr, size_t size);
extern void *Nu_DmaBufAlloc(size_t size, Int32 bUserAccess);


typedef void (*CAM_CALL_BACK)(void *pFrameBuffer);

typedef struct{
    Uint8 B;
    Uint8 G;
    Uint8 R;
    Uint8 I;
} PIXELS;

#include <zenr_api.h>

#define nu_CameraInit                zapi_Camera->Init
#define nu_CameraDeinit              zapi_Camera->Deinit
#define nu_CameraStart               zapi_Camera->Start
#define nu_CameraStop                zapi_Camera->Stop
#define nu_CameraAck                 zapi_Camera->Ack
#define nu_CameraShowFrame           zapi_Camera->ShowFrame
#define nu_CameraSetFlashLED         zapi_Camera->SetFlashLED
#define nu_CameraSetBrightness       zapi_Camera->SetBrightness
#define nu_CameraSetWhiteBalance     zapi_Camera->SetWhiteBalance
#define nu_CameraSetColoreffects     zapi_Camera->SetColoreffects
#define nu_CameraSetResolution       zapi_Camera->SetResolution
#define nu_CameraSetContrast         zapi_Camera->SetContrast
#define nu_CameraSetZoom             zapi_Camera->SetZoom
#define nu_CameraSetScenemode        zapi_Camera->SetScenemode

#define CAMERA_DATA_BYTES     2
#define CAMERA_SHOW_DATA_BYTES     4

#define LCD_WIDTH    240
#define LCD_HEIGHT   320
#define CameraBufferSum 2

typedef unsigned short Uint16 ;
typedef unsigned char Uint8 ;

static unsigned char *s_pShowFrameBuffer;
static unsigned char *s_pDisplayFrameBuffer;
static Uint16 *s_CameraAppBuffer[CameraBufferSum];

static UInt8 _color_table[256*3];
static const UInt8* color_table=&_color_table[256];

UInt8 border_color(long color)
{
    if (color>255)
        return 255;
    else if (color<0)
        return 0;
    else
        return (UInt8)color;
}

void Init_color_table()
{
    for (int i=0;i<256*3;++i)
        _color_table[i]=border_color(i-256);
}

const int csY_coeff_16 = (int)(1.164383*(1<<16));
const int csU_blue_16  = (int)(2.017232*(1<<16));
const int csU_green_16 = (int)((-0.391762)*(1<<16));
const int csV_green_16 = (int)((-0.812968)*(1<<16));
const int csV_red_16   = (int)(1.596027*(1<<16));

static int Ym_tableEx[256];
static int Um_blue_tableEx[256];
static int Um_green_tableEx[256];
static int Vm_green_tableEx[256];
static int Vm_red_tableEx[256];

static void Init_yuv_tableEx() {
    for (int i=0;i<256;++i)
    {
        Ym_tableEx[i]=(csY_coeff_16 * (i - 16) )>>16;
        Um_blue_tableEx[i]=(csU_blue_16 * (i - 128) )>>16;
        Um_green_tableEx[i]=(csU_green_16 * (i - 128) )>>16;
        Vm_green_tableEx[i]=(csV_green_16 * (i - 128) )>>16;
        Vm_red_tableEx[i]=(csV_red_16 * (i - 128) )>>16;
    }
}


static void TurnYUV_VToRGB565_H_90(Uint16 *pYUVSrc, Uint8 *pRGB565Dest, int iDesWidth, int iDesHeight)
{
    int si, di;
    int x, y;
    int Y[2], U, V;
    PIXELS *pRGB565Des = (PIXELS *)pRGB565Dest;
    Uint8 *pYUV;
    int iYUVFrameWidth = iDesHeight;
    int iYUVFrameHeight = iDesWidth;
    si = 0;
    for(y = 0, si = 0; y < iYUVFrameHeight; y++){
        for(x=0; x < iYUVFrameWidth; x+=2, si += 2){

            pYUV = (Uint8 *)(pYUVSrc+si);
            Y[0] = pYUV[0];
            U = pYUV[1];
            Y[1] = pYUV[2];
            V = pYUV[3];

            di = ((x * iYUVFrameHeight) + (iYUVFrameHeight - 1 - y));
            pRGB565Des[di].R = color_table[ Ym_tableEx[Y[0]] + Vm_red_tableEx[V] ];
            pRGB565Des[di].G = color_table[ Ym_tableEx[Y[0]] + Um_green_tableEx[U] + Vm_green_tableEx[V] ];
            pRGB565Des[di].B = color_table[ Ym_tableEx[Y[0]] + Um_blue_tableEx[U] ];

            di = (((x+1) * iYUVFrameHeight) + (iYUVFrameHeight - 1 - y));
            pRGB565Des[di].R = color_table[ Ym_tableEx[Y[1]] + Vm_red_tableEx[V] ];
            pRGB565Des[di].G = color_table[ Ym_tableEx[Y[1]] + Um_green_tableEx[U] + Vm_green_tableEx[V] ];
            pRGB565Des[di].B = color_table[ Ym_tableEx[Y[1]] + Um_blue_tableEx[U] ];

        }
    }

}

static void CleanDisplay()
{
    memset(s_pDisplayFrameBuffer, 0xff, (LCD_WIDTH*LCD_HEIGHT*CAMERA_SHOW_DATA_BYTES));
    nu_CameraShowFrame((Uint16 *)s_pDisplayFrameBuffer, LCD_WIDTH, LCD_HEIGHT);
}

static int ShowFrame(Uint16 *SrcDataBuffer)
{
    Uint16 *pFrameBuffer = SrcDataBuffer;
//    kprintf("ShowFrame SrcDataBuffer:0x%x.\n", pFrameBuffer);
    TurnYUV_VToRGB565_H_90((Uint16 *)pFrameBuffer, (Uint8 *)s_pDisplayFrameBuffer, LCD_WIDTH, LCD_HEIGHT);
    nu_CameraShowFrame((Uint16 *)s_pDisplayFrameBuffer, LCD_WIDTH, LCD_HEIGHT);
    return 0;
}

EXTERN_C int TICommandCameraInit()
{
    int i, iFrameSize;

    if(NULL == s_pShowFrameBuffer){
        s_pShowFrameBuffer = (BYTE *)Nu_DmaBufAlloc(
            (LCD_HEIGHT*LCD_WIDTH*CAMERA_DATA_BYTES*CameraBufferSum
            + LCD_HEIGHT*LCD_WIDTH*CAMERA_SHOW_DATA_BYTES), FALSE);
        if(NULL == s_pShowFrameBuffer){
            DRV_EXPRESSION(ERRORLEVEL,
                kprintf("TICommandCameraInit DzAllocKernelPages failed\n"));
            return 1;
        }
    }

    s_pDisplayFrameBuffer = s_pShowFrameBuffer
        + LCD_HEIGHT*LCD_WIDTH*CAMERA_DATA_BYTES*CameraBufferSum;

    Init_color_table();
    Init_yuv_tableEx();

    iFrameSize = 0;
    for(i = 0; i < CameraBufferSum; i++){
        s_CameraAppBuffer[i] = (Uint16 *)&s_pShowFrameBuffer[iFrameSize];
        kprintf("CameraInit s_CameraAppBuffer[%d] 0x%x.\n",i, s_CameraAppBuffer[i]);
        iFrameSize += LCD_HEIGHT*LCD_WIDTH*2;
    }

    if(0 != nu_CameraInit()){
        kprintf("Failed CameraInit nu_CameraInit.\n");
        return 1;
    }
    kprintf("Zener CameraInit OK\n");

    return 0;
}

void TICommandPreviewCallBackFunc(void *pFrameBuffer)
{
    ShowFrame((Uint16 *)pFrameBuffer);
    nu_CameraAck((Uint16 *)pFrameBuffer);
}

EXTERN_C int TICommandCameraBeginPreview()
{
    if(nu_CameraStart(
        CAM_MODE_PREVIEW,
        s_CameraAppBuffer,
        (Uint8)CameraBufferSum,
        LCD_WIDTH,
        LCD_HEIGHT,
        TICommandPreviewCallBackFunc)){
        kprintf("CameraBeginPreview nu_CameraStart failed\n");
        return 1;
    }
    kprintf("Zener CameraBeginPreview OK.\n");
    return 0;

}

EXTERN_C int TICommandCameraStopPreview()
{
    nu_CameraStop();
    CleanDisplay();
    kprintf("Zener CameraStopPreview OK.\n");
    return 0;
}

void TICommandSnapshotCallBackFunc(void *pFrameBuffer)
{
    ShowFrame((Uint16 *)pFrameBuffer);
}

EXTERN_C int TICommandCameraSnapshot()
{
    if(nu_CameraStart(
        CAM_MODE_SNAPSHOT,
        s_CameraAppBuffer,
        (Uint8)1,
        LCD_WIDTH,
        LCD_HEIGHT,
        TICommandSnapshotCallBackFunc)){
        kprintf("CameraSnapshot nu_CameraStart failed\n");
        return 1;
    }
    kprintf("Zener CameraSnapshot OK\n");
    return 0;
}

EXTERN_C int TICommandCameraDele()
{
    nu_CameraDeinit();
    if(NULL != s_pShowFrameBuffer){
        Nu_DmaBufFree((physaddr_t)s_pShowFrameBuffer,
            (LCD_HEIGHT*LCD_WIDTH*CAMERA_DATA_BYTES*CameraBufferSum
            + LCD_HEIGHT*LCD_WIDTH*CAMERA_SHOW_DATA_BYTES));
        s_pShowFrameBuffer = NULL;
    }

    kprintf("Zener CameraDele OK.\n");
    return 0;
}
